#include "scrambled_symbols.h"
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#if defined(WIN32)
#include <windows.h>
#include <winioctl.h>
#endif

#include <ctype.h>

#if defined(WIN32)
#include <Iphlpapi.h>
#endif

#if defined(linux)
#include <unistd.h>
#endif

#include "priv/mycomputer.hxx"
#include "conversion.h"
#include <string.h>

static const tByte NullByte=0;

MyComputer::MACAddress::MACAddress()
  : _mac()
{
  memset(_mac,0,6);
}

MyComputer::MACAddress::MACAddress(const tByte mac[6])
  : _mac()
{
  memcpy(_mac,mac,6);
}

MyComputer::MACAddress::MACAddress(const MyComputer::MACAddress &model)
  : _mac()
{
  memcpy(_mac,model._mac,6);  
}

MyComputer::MACAddress::~MACAddress()
{
  memset(_mac,0,6);
}

void MyComputer::MACAddress::Set(const tByte mac[6])
{
  memcpy(_mac,mac,6);
}

tByte &MyComputer::MACAddress::operator [] (tUInt32 index)
{
  if (index>=6) return (tByte &) NullByte;

  return _mac[index];
}

const tByte &MyComputer::MACAddress::operator [] (tUInt32 index) const
{
  if (index>=6) return (tByte &) NullByte;

  return _mac[index];
}


MyComputer::HardDisk::HardDisk() :
  _id(),
  _model(),
  _model_available(false),
  _serial(),
  _serial_available(false),
  _cachesize(),
  _cachesize_available(false),
  _disksize(),
  _disksize_available(false),
  _controllerindex((tUInt32) -1),
  _controllerindex_available(false),
  _controller_revision_number(),
  _controller_revision_number_available(false),
  _type(),
  _type_available(false)
{
}

MyComputer::HardDisk::HardDisk(const HardDisk &original) :
  _id(original._id),
  _model(original._model),
  _model_available(original._model_available),
  _serial(original._serial),
  _serial_available(original._serial_available),
  _cachesize(original._cachesize),
  _cachesize_available(original._cachesize_available),
  _disksize(original._disksize),
  _disksize_available(original._disksize_available),
  _controllerindex(original._controllerindex),
  _controllerindex_available(original._controllerindex_available),
  _controller_revision_number(original._controller_revision_number),
  _controller_revision_number_available(original._controller_revision_number_available),
  _type(original._type),
  _type_available(original._type_available)
{
}

MyComputer::HardDisk::~HardDisk() 
{ 
  Clear();
}

void MyComputer::HardDisk::Clear()
{
  _id=0;
  _id_available=false;
  _model.clear();
  _model_available=false;
  _serial.clear();
  _serial_available=false;
  _cachesize=0;
  _cachesize_available=false;
  _disksize=0;
  _disksize_available=false;
  _controllerindex=0;
  _controllerindex_available=false;
  _controller_revision_number.clear();
  _controller_revision_number_available=false;
  _type.clear();
  _type_available=false;
}

void MyComputer::HardDisk::SetId(tUInt32 id) 
{ 
  _id=id; 
  _id_available=true; 
}

tUInt32 MyComputer::HardDisk::GetId() const
{ 
  return _id;
}

bool MyComputer::HardDisk::IsIdAvailable() const 
{ 
  return _id_available; 
}

void MyComputer::HardDisk::SetModel(const string &model) 
{ 
  _model=model; 
  _model_available=true; 
}

const string &MyComputer::HardDisk::GetModel() const
{ 
  return _model;
}

bool MyComputer::HardDisk::IsModelAvailable() const 
{ 
  return _model_available; 
}

void MyComputer::HardDisk::SetSerial(const string &serial) 
{ 
  _serial=serial; 
  _serial_available=true; 
}

const string &MyComputer::HardDisk::GetSerial() const 
{ 
  return _serial; 
}

bool MyComputer::HardDisk::IsSerialAvailable() const 
{ 
  return _serial_available; 
}
 
void MyComputer::HardDisk::SetCacheSize(tUInt32 cachesize) 
{ 
  _cachesize=cachesize; 
  _cachesize_available=true; 
}

tUInt32 MyComputer::HardDisk::GetCacheSize() const 
{ 
  return _cachesize; 
}

bool MyComputer::HardDisk::IsCacheSizeAvailable() const 
{ 
  return _cachesize_available; 
}
   
void MyComputer::HardDisk::SetDiskSize(tUInt64 disksize) 
{ 
  _disksize=disksize; 
  _disksize_available=true; 
}

tUInt64 MyComputer::HardDisk::GetDiskSize() const
{ 
  return _disksize;
}

bool MyComputer::HardDisk::IsDiskSizeAvailable() const 
{ 
  return _disksize_available;
}

void MyComputer::HardDisk::SetControllerIndex(tUInt32 controller_index)
{
  _controllerindex=controller_index;
  _controllerindex_available=true;
}

tUInt32 MyComputer::HardDisk::GetControllerIndex() const
{
  return _controllerindex;
}

bool MyComputer::HardDisk::IsControllerIndexAvailable() const
{
  return _controllerindex_available;
}

void MyComputer::HardDisk::SetControllerRevisionNumber(const string &controller_revision_number) 
{ 
  _controller_revision_number=controller_revision_number; 
  _controller_revision_number_available=true; 
}

const string &MyComputer::HardDisk::GetControllerRevisionNumber() const 
{ 
  return _controller_revision_number; 
}

bool MyComputer::HardDisk::IsControllerRevisionNumberAvailable() const 
{ 
  return _controller_revision_number_available; 
}

void MyComputer::HardDisk::SetType(const string &type)
{
  _type=type;
  _type_available=true;
}

const string &MyComputer::HardDisk::GetType() const
{
  return _type;
}

bool MyComputer::HardDisk::IsTypeAvailable() const
{
  return _type_available;
}

Buffer &MyComputer::operator<<(Buffer &s, const MyComputer::MACAddress &mac)
{
  tUInt16 hexval;
  tByte *phexval;
  phexval=(tByte *) &hexval;

  for (tUInt32 i=0; i<6; i++)
    {
      hexval=Byte2Hex(mac[i]);
      s<<phexval[0]<<phexval[1];
      
      if (i<5) s<<"-";
    }
  return s;
}

Buffer &MyComputer::operator<<(Buffer &s, const MyComputer::HardDisk &hd)
{
  s<<"HardDisk[#="<<hd.GetId();

  if (hd.IsModelAvailable())
    {
      s<<", Model='"<<hd.GetModel()<<"'";
    }

  if (hd.IsSerialAvailable())
    {
      s<<", Serial='"<<hd.GetSerial()<<"'";
    }

  if (hd.IsCacheSizeAvailable())
    {
      s<<", CacheSize="<<hd.GetCacheSize()<<" bytes";
    }
  
  if (hd.IsDiskSizeAvailable())
    {
      s<<", DiskSize="<<hd.GetDiskSize()<<" bytes";
    }
  
  if (hd.IsControllerRevisionNumberAvailable())
    {
      s<<", ControllerRevisionNumber='"<<hd.GetControllerRevisionNumber()<<"'";
    }
  
  if (hd.IsTypeAvailable())
    {
      s<<", Type='"<<hd.GetType()<<"'";
    }

  s<<"]";

  return s;
}


ostream &MyComputer::operator <<(ostream &s, const MyComputer::MACAddress &mac)
{
  tUInt16 hexval;
  tByte *phexval;
  phexval=(tByte *) &hexval;

  for (tUInt32 i=0; i<6; i++)
    {
      hexval=Byte2Hex(mac[i]);
      s<<phexval[0]<<phexval[1];
      
      if (i<5) s<<"-";
    }
  return s;
}


ostream &MyComputer::operator<<(ostream &s, const MyComputer::HardDisk &hd)
{
  s<<"HardDisk[#="<<hd.GetId();

  if (hd.IsModelAvailable())
    {
      s<<", Model='"<<hd.GetModel()<<"'";
    }

  if (hd.IsSerialAvailable())
    {
      s<<", Serial='"<<hd.GetSerial()<<"'";
    }

  if (hd.IsCacheSizeAvailable())
    {
      s<<", CacheSize="<<hd.GetCacheSize()<<" bytes";
    }
  
  if (hd.IsDiskSizeAvailable())
    {
      s<<", DiskSize="<<hd.GetDiskSize()<<" bytes";
    }
  
  if (hd.IsControllerRevisionNumberAvailable())
    {
      s<<", ControllerRevisionNumber='"<<hd.GetControllerRevisionNumber()<<"'";
    }
  
  if (hd.IsTypeAvailable())
    {
      s<<", Type='"<<hd.GetType()<<"'";
    }

  s<<"]";

  return s;
}


// This function has been highly inspired from Lynn McGuire's Diskid32 
// utility source code. I modified it a little bit to match my own 
// coding conventions and functionality.
tUInt32 MyComputer::GetMACAddresses(MyComputer::tMACAddressList &maclist)
{
#if defined(WIN32)
  IP_ADAPTER_INFO AdapterInfo[16];       // Allocate information
                                         // for up to 16 NICs
  DWORD dwBufLen;
  DWORD dwStatus;
  PIP_ADAPTER_INFO pAdapterInfo;
  MyComputer::MACAddress mac;

  maclist.clear();

  dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer

  dwStatus = GetAdaptersInfo(               // Call GetAdapterInfo
			     AdapterInfo,   // [out] buffer to receive data
			     &dwBufLen);    // [in] size of receive data buffer

  if (dwStatus != ERROR_SUCCESS) { return (tUInt32) -1; }  // Verify return value is
                                                // valid, no buffer overflow

  pAdapterInfo = AdapterInfo; // Contains pointer to
                              // current adapter info
  do 
    {
      mac.Set(pAdapterInfo->Address);
      maclist.push_back(mac);
      pAdapterInfo = pAdapterInfo->Next;    // Progress through linked list
  } while(pAdapterInfo!=0);                    // Terminate if last adapter
#elif defined(linux)
  maclist.clear();
#else
#error "OS Not supported"
#endif
  return maclist.size();

}

#if defined(WIN32)
#include "mycomputer.windows.inc"
#endif

tUInt32 MyComputer::GetHardDisks(MyComputer::tHardDiskList &hdlist)
{
  hdlist.clear();
#if defined(WIN32)
  return getHardDriveComputerID(hdlist);
#elif defined(linux)
  return 0;
#else
#error "OS Not supported"
#endif
}

tUInt32 MyComputer::GetComputerId()
{
#if defined(WIN32)
  if (_computerid== (tUInt32) -1)
    {
      MyComputer::tHardDiskList hdlist;
      
      // This call will set _computerid;
      if (getHardDriveComputerID(hdlist)==(tUInt32) -1)
	{
	  return (tUInt32) -1;
	}
    }
  return _computerid;

#elif defined(linux)
  return gethostid();
#else
#error "OS Not supported"
#endif

}
